﻿using System.Collections.Generic;
using Metro.Kashikoi.Common.ExceptionHandling;
using Metro.Kashikoi.Service.Common.Entity;
using Metro.Kashikoi.Service.Location.Entity;
using Metro.Kashikoi.ServiceContract.Location;

namespace Metro.Kashikoi.Service.Location.Business
{
    /// <summary>
    /// This class implements the <c>IFacade</c>.
    /// </summary>
    class Facade : IFacade
    {
        #region IFacade Members

        /// <summary>
        /// Creates the organization.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="organization">The organization.</param>
        /// <returns>
        /// The newly created organization.
        /// </returns>
        public void CreateOrganization(IUnitOfWork context, Organization organization)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, organization);
            organization.Deleted = false;
            rep.Add(organization);
        }

        /// <summary>
        /// Updates the organization.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="organization">The organization.</param>
        public void UpdateOrganization(IUnitOfWork context, Organization organization)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, organization);
            var org = rep.GetEntityById<Organization>(organization.Id);
            org.Name = organization.Name;
            org.Address = organization.Address;
        }

        /// <summary>
        /// Deleles the organization.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="organizationId">The organization id.</param>
        /// <param name="deletedStockArea">The deleted stock area.</param>
        public void DeleleOrganization(IUnitOfWork context, int organizationId, IList<int> deletedStockArea)
        {
            var rep = new Repository<Region>(context);

            var entity = rep.GetEntityById<Organization>(organizationId);
            foreach (var child in entity.Facilities)
            {
                DeleteRegion(child, deletedStockArea);
            }
            entity.Deleted = true;
        }

        /// <summary>
        /// Gets the default organization.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>
        /// The default organization.
        /// </returns>
        public Organization GetDefaultOrganization(IUnitOfWork context)
        {
            var rep = new Repository<Region>(context);

            var org = rep.GetEntity<Organization>(o => o.Deleted == false);
            if (org == null)
            {
                throw new NotExistException
                {
                    Entity = "Organization",
                    Property = "First",
                    Value = 0,
                };
            }
            FetchChild(org);
            return org;
        }

        /// <summary>
        /// Gets the organization with specific name.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="name">The name.</param>
        /// <returns>
        /// The organization with specific name.
        /// </returns>
        public Organization GetOrganization(IUnitOfWork context, string name)
        {
            var rep = new Repository<Region>(context);

            var org = rep.GetEntity<Organization>(o => o.Name == name && o.Deleted == false);
            if (org == null)
            {
                throw new NotExistException
                {
                    Entity = "Organization",
                    Property = "Name",
                    Value = name,
                };
            }
            FetchChild(org);
            return org;
        }

        /// <summary>
        /// Creates the facility.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="facility">The facility.</param>
        /// <param name="organizationId">The organization id.</param>
        /// <returns>
        /// The newly created facility.
        /// </returns>
        public void CreateFacility(IUnitOfWork context, Facility facility, int organizationId)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, facility);
            var org = rep.GetEntityById<Organization>(organizationId);
            facility.Organization = org;
            facility.Deleted = false;
            rep.Add(facility);
        }

        /// <summary>
        /// Updates the facility.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="facility">The facility.</param>
        public void UpdateFacility(IUnitOfWork context, Facility facility)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, facility);
            var entity = rep.GetEntityById<Facility>(facility.Id);
            entity.Name = facility.Name;
            entity.Address = facility.Address;
        }

        /// <summary>
        /// Deletes the facility.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="facilityId">The facility id.</param>
        /// <param name="deletedStockArea">The deleted stock area.</param>
        public void DeleteFacility(IUnitOfWork context, int facilityId, IList<int> deletedStockArea)
        {
            var rep = new Repository<Region>(context);

            var facility = rep.GetEntityById<Facility>(facilityId);
            DeleteRegion(facility, deletedStockArea);
        }

        /// <summary>
        /// Creates the unit.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="unit">The unit.</param>
        /// <param name="facilityId">The facility id.</param>
        public void CreateUnit(IUnitOfWork context, Unit unit, int facilityId)
        {
            var repFac = new Repository<Region>(context);
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, unit);
            var facility = rep.GetEntityById<Facility>(facilityId);
            unit.Facility = facility;
            rep.Add(unit);
        }

        /// <summary>
        /// Updates the unit.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="unit">The unit.</param>
        public void UpdateUnit(IUnitOfWork context, Unit unit)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, unit);
            var entity = rep.GetEntityById<Unit>(unit.Id);
            entity.Name = unit.Name;
        }

        /// <summary>
        /// Deletes the unit.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="unitId">The unit id.</param>
        /// <param name="deletedStockArea">The deleted stock area.</param>
        public void DeleteUnit(IUnitOfWork context, int unitId, IList<int> deletedStockArea)
        {
            var rep = new Repository<Region>(context);

            var unit = rep.GetEntityById<Unit>(unitId);
            DeleteRegion(unit, deletedStockArea);
        }

        /// <summary>
        /// Creates the stock area.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="stockArea">The stock area.</param>
        /// <param name="unitId">The unit id.</param>
        /// <returns>
        /// The newly created stock area.
        /// </returns>
        public void CreateStockArea(IUnitOfWork context, StockArea stockArea, int unitId)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, stockArea);
            var unit = rep.GetEntityById<Unit>(unitId);
            stockArea.Unit = unit;
            stockArea.Deleted = false;
            rep.Add(stockArea);
        }

        /// <summary>
        /// Updates the stock area.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="stockArea">The stock area.</param>
        public void UpdateStockArea(IUnitOfWork context, StockArea stockArea)
        {
            var rep = new Repository<Region>(context);

            CheckDuplicate(rep, stockArea);
            var entity = rep.GetEntityById<StockArea>(stockArea.Id);
            entity.Name = stockArea.Name;
        }

        /// <summary>
        /// Deletes the stock area.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="stockAreaId">The stock area id.</param>
        public void DeleteStockArea(IUnitOfWork context, int stockAreaId)
        {
            var rep = new Repository<Region>(context);
            IList<int> stockAreaIdList = new List<int>();

            var entity = rep.GetEntityById<StockArea>(stockAreaId);
            DeleteRegion(entity, stockAreaIdList);
        }

        /// <summary>
        /// Gets the stock area.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="stockAreaId">The stock area id.</param>
        /// <returns>
        /// The stock area.
        /// </returns>
        public StockArea GetStockArea(IUnitOfWork context, int stockAreaId)
        {
            var rep = new Repository<Region>(context);
            return rep.GetEntityById<StockArea>(stockAreaId);
        }

        #endregion

        #region Private Methods

        private static void CheckDuplicate(Repository<Region> rep, Region entity)
        {
            if (rep.Count(o => o.Name == entity.Name && o.Deleted == false) > 0)
            {
                throw new DuplicateException()
                {
                    Entity = entity.GetType().Name,
                    Property = "Name",
                    Value = entity.Name,
                };
            }
        }

        private void DeleteRegion(Region region, IList<int> stockAreaIdList)
        {
            region.Deleted = true;

            if (region is StockArea)
            {
                stockAreaIdList.Add(region.Id);
            }
            else
            {
                foreach (var child in region.Children)
                {
                    DeleteRegion(child, stockAreaIdList);
                }
            }
        }

        private static void FetchChild(Region region)
        {
            if (region.Children != null && region.Children.Count > 0)
            {
                List<Region> deleted = new List<Region>();
                foreach (var child in region.Children)
                {
                    if (child.Deleted)
                    {
                        deleted.Add(child);
                    }
                    else
                    {
                        FetchChild(child);
                    }
                }
                foreach (var del in deleted)
                {
                    region.Children.Remove(del);
                }
            }
        }

        #endregion
    }
}
